#include "srv.h"

/*	
 *  Implementacion de servidor con comunicacion con sus pares. 
 */
 
void servidor(int mi_cliente)
{
    MPI_Status status; int origen, tag;
		/// n es la cantidad de procesos (contando servidores y clientes).
    int n = cant_ranks;
		/// cant_activos es la cantidad de procesos activos
	int cant_activos = n;
		/// hay_pedido_local indica si hay pedido de mutex de parte del cliente.
    int hay_pedido_local = FALSE;
		/// listo_para_salir indica que el cliente finalizo.
    int listo_para_salir = FALSE;
		/// nro_seq_recibido va a tener el numero de secuencia que recibo como parametro (eventualmente) en los mensajes.
		/// es el unico parametro que (puedo) recibo(-ir)
    int nro_seq_recibido;
		/// reply_pending: las respuestas que me faltan para poder acceder al mutex.
    int reply_pending;
		/// highest: la maxima secuencia recibida.
    int highest = 0;
		/// defer: indica si hay que diferir o no la respuesta a otro servidor.
    int defer;
		/// reply_deferred: un array con posiciones para cada servidor que dice si tengo una respuesta pendiente para cada uno de ellos.
    int reply_deferred[n];
		/// our_sn: nuestro numero de secuencia.
    int our_sn;
		/// me: el rank de mi cliente.
    int me = mi_cliente - 1;
		/// servidores_activos: true si aun no termino, ow false.
    int servidores_activos[n];
    int j,k;
		/// inicializo los arreglos
    for(j=0; j<n; j++)
		reply_deferred[j] = FALSE;
	for(j=0; j<n; j++)
		servidores_activos[j] = TRUE;
    
		/// mientras no haya terminado
    while( ! listo_para_salir ) {
        
        MPI_Recv(&nro_seq_recibido, 1, MPI_INT, ANY_SOURCE, ANY_TAG, COMM_WORLD, &status);
			/// espero mensajes de cualquiera y me guardo en nro_seq_recibido el eventual parametro que me pasen.
			/// (si no me pasan ningun parametro no hay problema ya que el '1' me indica la cantidad maxima de parametros que recibire)
			
			/// me guardo en origen quien me lo envia y en tag el tag que me mandaron.
        origen = status.MPI_SOURCE;
        tag = status.MPI_TAG;
			
			/// si me lo envio mi cliente:
        if(origen == mi_cliente){
				/// si me pide el mutex:
			if (tag == TAG_PEDIDO) {
				
				assert(origen == mi_cliente);
				debug("Mi cliente solicita acceso exclusivo");
				assert(hay_pedido_local == FALSE);
				hay_pedido_local = TRUE;
				
					/// nuestro numero de secuencia sera el maximo que teniamos + 1
				our_sn = highest+1;
					/// tengo n/2 servidores entonces voy a esperar la respuesta de todos ellos (menos yo)
				reply_pending = cant_activos/2 - 1;
					
					/// si reply_pending es 0 entonces estoy solo, ergo no le tengo que enviar nada a nadie. Entro directo al mutex:
				if(reply_pending == 0){
					debug("Dandole permiso");
					MPI_Send(NULL, 0, MPI_INT, mi_cliente, TAG_OTORGADO, COMM_WORLD);
				}else{
						/// sino, tengo que enviarles a todos los servidores activos un request.
					for(j=0; j< n; j+=2){
						if(j != me && servidores_activos[j]){
							int my_seq = our_sn;
							//~ int MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
							//~ printf("%d %d AAAAAAAAAAAAAAAAAAAAAAAAAA\n", j, me);
							MPI_Send(&my_seq, 1, MPI_INT, j, TAG_REQUEST, COMM_WORLD);
							//~ printf("BBBBBBBBBBBBBBBBBBBBBBBBBB\n");
	
							//~ MPI_Send(REQUEST(our_sn,me),j);
						}
					}							
				}
			}else if (tag == TAG_LIBERO) {
					/// si quiere liberarlo:z
				assert(origen == mi_cliente);
				assert(hay_pedido_local == TRUE);
				for(j=0; j<n; j+=2){
						/// me fijo para cada servidor (por eso el +=2, me salteo los clientes) si tenia diferida su respuesta
					if(reply_deferred[j]){
						/// si la tenia, le mando la respuesta.
						reply_deferred[j] = FALSE;
						MPI_Send(NULL, 0, MPI_INT, j, TAG_REPLY, COMM_WORLD);
					}
				}
				debug("Mi cliente libera su acceso exclusivo");
				hay_pedido_local = FALSE;
			}
			
			else if (tag == TAG_TERMINE) {
					/// mi cliente termina, no va a usar mas mutex:
				assert(origen == mi_cliente);
					/// envio mensaje a mis pares de que termine
				for(j=0; j<n; j+=2){
					if(j != m && servidores_activos[j]){
						MPI_Send(NULL, 0, MPI_INT, j, TAG_MEFUI, COMM_WORLD);
					}
				}	
				debug("Mi cliente avisa que termino");
				listo_para_salir = TRUE;
			}
		}else{
			
			/// sino, me lo envio un servidor:
			if(tag == TAG_REQUEST){
					/// otro servidor me pide acceso al mutex.
				j = origen;
				k = nro_seq_recibido;
				if (highest < k) highest = k;
					/// voy a diferir si:
						/// mi cliente tiene que haber pedido el mutex. Si no lo hizo entonces no hay motivo para no responder.
						/// Ademas el numero de secuencia recibido tiene que ser mayor que el nuestro
						/// o si son iguales, su rank tiene que ser mayor que el nuestro.
				defer = hay_pedido_local && ((k>our_sn) || (k==our_sn && j>me));
				//~ printf("K= %d hay_pedido_local %d OUR_SN= %d J= %d   ME= %d \n ", k, hay_pedido_local, our_sn, j, me);
				if(defer){
						/// Si diferi, pongo como true en el arreglo.
					//~ printf("Deferi a %d\n", j);
					reply_deferred[j] = TRUE;
				}else{
						/// Sino, respondo.
					MPI_Send(NULL, 0, MPI_INT, j, TAG_REPLY, COMM_WORLD);
				}
			}else if(tag == TAG_REPLY){
					/// otro servidor me envia un reply
					/// tengo una respuesta pendiente menos
				reply_pending --;
				if(reply_pending == 0){
					/// Si es la ultima que esperaba, mi cliente tiene acceso al mutex.
					debug("Dandole permiso");
					MPI_Send(NULL, 0, MPI_INT, mi_cliente, TAG_OTORGADO, COMM_WORLD);
				}
			}
			else if(tag == TAG_MEFUI){
					/// un servidor se fue, no esta mas activo.
				cant_activos -= 2;
				servidores_activos[j] = FALSE;
			}		
		}
	}
}
